package com.ruyiadmin.springcloud.producer.controller.system;

import com.alibaba.fastjson.JSON;
import com.ruyiadmin.springcloud.producer.common.annotations.system.Log;
import com.ruyiadmin.springcloud.producer.common.annotations.system.Permission;
import com.ruyiadmin.springcloud.producer.common.beans.system.SystemCacheConfig;
import com.ruyiadmin.springcloud.producer.common.components.core.RuYiRedisComponent;
import com.ruyiadmin.springcloud.producer.common.core.business.enums.DeletionType;
import com.ruyiadmin.springcloud.producer.common.core.business.enums.OperationType;
import com.ruyiadmin.springcloud.producer.common.core.system.entities.ActionResult;
import com.ruyiadmin.springcloud.producer.common.core.system.entities.QueryResult;
import com.ruyiadmin.springcloud.producer.common.exceptions.RuYiAdminCustomException;
import com.ruyiadmin.springcloud.producer.domain.dto.system.SysMenuDTO;
import com.ruyiadmin.springcloud.producer.domain.entity.system.SysLanguage;
import com.ruyiadmin.springcloud.producer.domain.entity.system.SysMenuLanguage;
import com.ruyiadmin.springcloud.producer.service.iservices.system.ISysMenuLanguageService;
import com.ruyiadmin.springcloud.producer.service.iservices.system.ISysMenuService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单表 前端控制器
 * </p>
 *
 * @author RuYiAdmin
 * @since 2022-07-12
 */
@RestController
@RequestMapping("/MenuManagement")
@Api(tags = "系统菜单管理服务")
@RequiredArgsConstructor
public class SysMenuController {

    //region 服务私有属性

    private final RuYiRedisComponent redisUtils;
    private final SystemCacheConfig systemCacheConfig;
    private final ISysMenuService menuService;
    private final ISysMenuLanguageService menuLanguageService;

    //endregion

    //region 查询菜单列表

    @PostMapping("/Post")
    @ApiOperation(value = "查询菜单列表")
    @Log(OperationType = OperationType.QueryList)
    @Permission(permission = "menu:query:list")
    public QueryResult<SysMenuDTO> getMenuTreeNodes() throws ExecutionException, InterruptedException {
        CompletableFuture<QueryResult<SysMenuDTO>> future =
                CompletableFuture.supplyAsync(this.menuService::getMenuTreeNodes);
        return future.get();
    }

    //endregion

    //region 获取菜单信息

    @GetMapping("/GetById/{id}")
    @ApiOperation(value = "获取菜单信息")
    @Log(OperationType = OperationType.QueryEntity)
    @Permission(permission = "menu:query:list")
    public ActionResult getById(@PathVariable("id") String id) throws ExecutionException, InterruptedException {
        CompletableFuture<ActionResult> future = CompletableFuture.supplyAsync(() -> {
            Object value = this.redisUtils.get(this.systemCacheConfig.getMenuCacheName());
            List<SysMenuDTO> menus = JSON.parseArray(value.toString(), SysMenuDTO.class);

            SysMenuDTO menu = menus.stream().filter(t -> t.getId().equals(id)).
                    collect(Collectors.toList()).get(0);

            //region 初始化菜单多语

            value = this.redisUtils.get(this.systemCacheConfig.getLanguageCacheName());
            List<SysLanguage> languages = JSON.parseArray(value.toString(), SysLanguage.class);

            SysLanguage lanEn = languages.stream().
                    filter(t -> t.getLanguageName().equals("en-US")).
                    collect(Collectors.toList()).get(0);
            SysLanguage lanRu = languages.stream().
                    filter(t -> t.getLanguageName().equals("ru-RU")).
                    collect(Collectors.toList()).get(0);

            value = this.redisUtils.get(this.systemCacheConfig.getMenuAndLanguageCacheName());
            List<SysMenuLanguage> menuLanguages = JSON.parseArray(value.toString(), SysMenuLanguage.class);

            //region 初始化多语

            List<SysMenuLanguage> enMenu = menuLanguages.stream().
                    filter(t -> t.getMenuId().equalsIgnoreCase(menu.getId())).
                    filter(t -> t.getLanguageId().equalsIgnoreCase(lanEn.getId())).
                    collect(Collectors.toList());

            List<SysMenuLanguage> ruMenu = menuLanguages.stream().
                    filter(t -> t.getMenuId().equalsIgnoreCase(menu.getId())).
                    filter(t -> t.getLanguageId().equalsIgnoreCase(lanRu.getId())).
                    collect(Collectors.toList());

            if (enMenu.size() > 0) {
                menu.setMenuNameEn(enMenu.get(0).getMenuName());
            }

            if (ruMenu.size() > 0) {
                menu.setMenuNameRu(ruMenu.get(0).getMenuName());
            }

            //endregion

            //endregion

            return ActionResult.success(menu);
        });
        return future.get();
    }

    //endregion

    //region 新增菜单信息

    @PostMapping("/Add")
    @ApiOperation(value = "新增菜单信息")
    @Log(OperationType = OperationType.AddEntity)
    @Permission(permission = "menu:add:entity")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult addMenu(@Valid @RequestBody SysMenuDTO sysMenuDTO) {
        ActionResult actionResult = ActionResult.success(this.menuService.save(sysMenuDTO));

        //region 维护菜单多语

        Object value = this.redisUtils.get(this.systemCacheConfig.getLanguageCacheName());
        List<SysLanguage> languages = JSON.parseArray(value.toString(), SysLanguage.class);

        value = this.redisUtils.get(this.systemCacheConfig.getMenuAndLanguageCacheName());
        List<SysMenuLanguage> menuLanguages = JSON.parseArray(value.toString(), SysMenuLanguage.class);

        if (!StringUtils.isEmpty(sysMenuDTO.getMenuNameEn())) {
            SysLanguage language = languages.stream().
                    filter(t -> t.getLanguageName().equals("en-US")).
                    collect(Collectors.toList()).get(0);

            SysMenuLanguage menuLan = new SysMenuLanguage();
            menuLan.setLanguageId(language.getId());
            menuLan.setMenuId(sysMenuDTO.getId());
            menuLan.setMenuName(sysMenuDTO.getMenuNameEn());
            this.menuLanguageService.save(menuLan);

            menuLanguages.add(menuLan);
        }

        if (!StringUtils.isEmpty(sysMenuDTO.getMenuNameRu())) {
            SysLanguage language = languages.stream().
                    filter(t -> t.getLanguageName().equals("ru-RU")).
                    collect(Collectors.toList()).get(0);

            SysMenuLanguage menuLan = new SysMenuLanguage();
            menuLan.setLanguageId(language.getId());
            menuLan.setMenuId(sysMenuDTO.getId());
            menuLan.setMenuName(sysMenuDTO.getMenuNameRu());
            this.menuLanguageService.save(menuLan);

            menuLanguages.add(menuLan);
        }

        //endregion

        //region 数据一致性维护

        value = this.redisUtils.get(this.systemCacheConfig.getMenuCacheName());
        List<SysMenuDTO> menus = JSON.parseArray(value.toString(), SysMenuDTO.class);

        if (sysMenuDTO.getChildren() != null) {
            sysMenuDTO.getChildren().clear();
        }
        menus.add(sysMenuDTO);

        //更新缓存
        this.redisUtils.set(this.systemCacheConfig.getMenuCacheName(), JSON.toJSONString(menus));
        this.redisUtils.set(this.systemCacheConfig.getMenuAndLanguageCacheName(), JSON.toJSONString(menuLanguages));

        //endregion

        return actionResult;
    }

    //endregion

    //region 修改菜单信息

    @PutMapping("/Put")
    @ApiOperation(value = "修改菜单信息")
    @Log(OperationType = OperationType.EditEntity)
    @Permission(permission = "menu:edit:entity")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult editMenu(@Valid @RequestBody SysMenuDTO sysMenuDTO) {
        ActionResult actionResult = ActionResult.success(this.menuService.updateById(sysMenuDTO));

        //region 维护菜单多语

        Object value = this.redisUtils.get(this.systemCacheConfig.getLanguageCacheName());
        List<SysLanguage> languages = JSON.parseArray(value.toString(), SysLanguage.class);

        value = this.redisUtils.get(this.systemCacheConfig.getMenuAndLanguageCacheName());
        List<SysMenuLanguage> menuLanguages = JSON.parseArray(value.toString(), SysMenuLanguage.class);

        if (!StringUtils.isEmpty(sysMenuDTO.getMenuNameEn())) {
            SysLanguage language = languages.stream().
                    filter(t -> t.getLanguageName().equals("en-US")).
                    collect(Collectors.toList()).get(0);

            List<SysMenuLanguage> list = menuLanguages.stream().
                    filter(t -> t.getLanguageId().equalsIgnoreCase(language.getId())).
                    filter(t -> t.getMenuId().equalsIgnoreCase(sysMenuDTO.getId())).
                    filter(t -> t.getIsdel() == DeletionType.Undeleted.ordinal()).
                    collect(Collectors.toList());
            SysMenuLanguage oldMenuLan = list.size() > 0 ? list.get(0) : null;
            if (oldMenuLan != null && !oldMenuLan.getMenuName().equals(sysMenuDTO.getMenuNameEn())) {
                //删除就数据
                this.menuLanguageService.removeById(oldMenuLan.getId());
                for (Iterator<SysMenuLanguage> iterator = menuLanguages.iterator(); iterator.hasNext(); ) {
                    SysMenuLanguage element = iterator.next();
                    if (element.getId().equals(oldMenuLan.getId())) {
                        iterator.remove();
                        break;
                    }
                }
                //添加新数据
                SysMenuLanguage menuLan = new SysMenuLanguage();
                menuLan.setLanguageId(language.getId());
                menuLan.setMenuId(sysMenuDTO.getId());
                menuLan.setMenuName(sysMenuDTO.getMenuNameEn());
                this.menuLanguageService.save(menuLan);
                menuLanguages.add(menuLan);
            }
        }
        if (!StringUtils.isEmpty(sysMenuDTO.getMenuNameRu())) {
            SysLanguage language = languages.stream().
                    filter(t -> t.getLanguageName().equals("ru-RU")).
                    collect(Collectors.toList()).get(0);

            List<SysMenuLanguage> list = menuLanguages.stream().
                    filter(t -> t.getLanguageId().equalsIgnoreCase(language.getId())).
                    filter(t -> t.getMenuId().equalsIgnoreCase(sysMenuDTO.getId())).
                    filter(t -> t.getIsdel() == DeletionType.Undeleted.ordinal()).
                    collect(Collectors.toList());
            SysMenuLanguage oldMenuLan = list.size() > 0 ? list.get(0) : null;
            if (oldMenuLan != null && !oldMenuLan.getMenuName().equals(sysMenuDTO.getMenuNameRu())) {
                //删除就数据
                this.menuLanguageService.removeById(oldMenuLan.getId());
                for (Iterator<SysMenuLanguage> iterator = menuLanguages.iterator(); iterator.hasNext(); ) {
                    SysMenuLanguage element = iterator.next();
                    if (element.getId().equals(oldMenuLan.getId())) {
                        iterator.remove();
                        break;
                    }
                }
                //添加新数据
                SysMenuLanguage menuLan = new SysMenuLanguage();
                menuLan.setLanguageId(language.getId());
                menuLan.setMenuId(sysMenuDTO.getId());
                menuLan.setMenuName(sysMenuDTO.getMenuNameRu());
                this.menuLanguageService.save(menuLan);
                menuLanguages.add(menuLan);
            }
        }

        //endregion

        //region 数据一致性维护

        value = this.redisUtils.get(this.systemCacheConfig.getMenuCacheName());
        List<SysMenuDTO> menus = JSON.parseArray(value.toString(), SysMenuDTO.class);

        //删除就数据
        SysMenuDTO menu = menus.stream().
                filter(t -> t.getId().equals(sysMenuDTO.getId())).
                collect(Collectors.toList()).
                get(0);
        for (Iterator<SysMenuDTO> iterator = menus.iterator(); iterator.hasNext(); ) {
            SysMenuDTO element = iterator.next();
            if (element.getId().equals(menu.getId())) {
                iterator.remove();
                break;
            }
        }

        //添加新数据
        if (sysMenuDTO.getChildren() != null) {
            sysMenuDTO.getChildren().clear();
        }
        menus.add(sysMenuDTO);

        //更新缓存
        this.redisUtils.set(this.systemCacheConfig.getMenuCacheName(), JSON.toJSONString(menus));
        this.redisUtils.set(this.systemCacheConfig.getMenuAndLanguageCacheName(), JSON.toJSONString(menuLanguages));

        //endregion

        return actionResult;
    }

    //endregion

    //region 批量删除菜单信息

    @DeleteMapping("/DeleteRange/{ids}")
    @ApiOperation(value = "批量删除菜单信息")
    @Log(OperationType = OperationType.DeleteEntity)
    @Permission(permission = "menu:del:entities")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult deleteRange(@PathVariable("ids") String ids) {

        //region 批量删除菜单信息

        //机构删除检测
        this.deleteCheck(ids);
        //删除菜单数据
        List<String> array = Arrays.asList(ids.split(","));
        ActionResult actionResult = ActionResult.success(this.menuService.removeByIds(array));
        //删除多语信息
        Object value = this.redisUtils.get(this.systemCacheConfig.getMenuAndLanguageCacheName());
        List<SysMenuLanguage> menuLanguages = JSON.parseArray(value.toString(), SysMenuLanguage.class);
        for (String item : array) {
            List<SysMenuLanguage> oldMenuLans = menuLanguages.stream().
                    filter(t -> t.getMenuId().equals(item)).
                    filter(t -> t.getIsdel() == DeletionType.Undeleted.ordinal()).
                    collect(Collectors.toList());
            if (oldMenuLans.size() > 0) {
                //删除数据库多语
                oldMenuLans.forEach(t -> this.menuLanguageService.removeById(t.getId()));
                //删除缓存多语
                oldMenuLans.forEach(t -> {
                    for (Iterator<SysMenuLanguage> iterator = menuLanguages.iterator(); iterator.hasNext(); ) {
                        SysMenuLanguage element = iterator.next();
                        if (element.getId().equals(t.getId())) {
                            iterator.remove();
                            break;
                        }
                    }
                });
            }
        }

        //endregion

        //region 数据一致性维护

        value = this.redisUtils.get(this.systemCacheConfig.getMenuCacheName());
        List<SysMenuDTO> menus = JSON.parseArray(value.toString(), SysMenuDTO.class);

        //从缓存删除菜单信息
        array.forEach(t -> {
            for (Iterator<SysMenuDTO> iterator = menus.iterator(); iterator.hasNext(); ) {
                SysMenuDTO element = iterator.next();
                if (element.getId().equals(t)) {
                    iterator.remove();
                    break;
                }
            }
        });

        //更新缓存
        this.redisUtils.set(this.systemCacheConfig.getMenuCacheName(), JSON.toJSONString(menus));
        this.redisUtils.set(this.systemCacheConfig.getMenuAndLanguageCacheName(), JSON.toJSONString(menuLanguages));

        //endregion

        return actionResult;

    }

    //endregion

    //region 菜单删除检测

    /**
     * 菜单删除检测
     *
     * @param ids 编号数组
     */
    private void deleteCheck(String ids) {
        Object value = this.redisUtils.get(this.systemCacheConfig.getMenuCacheName());
        List<SysMenuDTO> menus = JSON.parseArray(value.toString(), SysMenuDTO.class);

        String[] array = ids.split(",");
        //删除校验
        for (String item : array) {
            List<SysMenuDTO> subMenus = menus.stream().
                    filter(t -> !StringUtils.isEmpty(t.getParentId())).
                    filter(t -> t.getParentId().equals(item)).
                    collect(Collectors.toList());
            if (subMenus.size() > 0) {
                throw new RuYiAdminCustomException("menu contains subitems,can not be deleted");
            }
        }
    }

    //endregion

}
